package cate.game.role.bag.rune;

import cate.common.table.d.GDObj;
import cate.common.table.d.GDRune;
import cate.common.table.fight.skill.row.SkillBaseRow;
import cate.common.table.item.rune.SkillGroupList;
import cate.common.table.item.rune.row.*;
import cate.common.util.GameResult;
import cate.common.util.random.RWItem;
import cate.common.util.random.RWList;
import cate.game.GameBody;
import cate.game.attr.FightAttr;
import cate.game.play.support.PlayBuildContext;
import cate.game.role.Role;
import cate.game.role.bag.item.BaseBagItem;
import com.fasterxml.jackson.annotation.JsonIgnore;
import cp.solution.pattern.ICopiable;
import cp.solution.util.collection.ListKit;
import easy.java.dev.note.NoteClass;
import easy.java.dev.note.NoteField;
import easy.java.practice.errorcode.EcResult;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@NoteClass("徽章数据")
public class Rune extends BaseBagItem implements ICopiable<Rune> {
    @NoteField(value = "基础属性id们")
    public List<Integer> attrIds;

    @NoteField(value = "技能id们")
    public List<Integer> skillIds;

    @NoteField(value = "徽章重铸技能、属性的缓存")
    public ReCastTemp reCast;

    @NoteField(value = "徽章当前的经验")
    public int exp;

    @NoteField(value = "进阶专属属性id们")
    public List<Integer> gradeAttrIds;

    @NoteField(value = "是否被锁定")
    public boolean lock;

    @NoteField(value = "强化等级")
    public int level;

    public Rune() {

    }

    public Rune(Role role, int tid, long num) {
        super(role, GDObj.Type.RUNE, tid, num);
        this.skillIds = this.genSkill(role.getGame());
        this.attrIds = this.genAttr(role.getGame());
        this.reCast = new ReCastTemp();
        this.reCast.initialize();
        this.gradeAttrIds = new ArrayList<>();
    }

    @Override
    public void initialize(Role role) {
        super.initialize(role);
        if (attrIds == null) {
            attrIds = new ArrayList<>();
        }
        if (skillIds == null) {
            skillIds = new ArrayList<>();
        }
        if (reCast == null) {
            reCast = new ReCastTemp();
        }
        reCast.initialize();
        if (gradeAttrIds == null) {
            gradeAttrIds = new ArrayList<>();
        }
    }

    /**
     * 随机生成技能(默认生成普通技能)
     */
    @JsonIgnore
    public List<Integer> genSkill(GameBody game) {
        return genSkill(game, false, 0);
    }

    public List<Integer> genLuckyDoubleSkill(GameBody game) {
        RuneBaseRow row = game.table.rune.base.get(tid);
        if (row.niubiRecastGroup1 == 0 || row.niubiRecastGroup2 == 0) {
            return Collections.emptyList();
        }
        List<Integer> groups = new ArrayList<>();
        groups.add(row.niubiRecastGroup1);
        groups.add(row.niubiRecastGroup2);
        return genSkill(game, groups, null);
    }

    public List<Integer> genLuckySkill(GameBody game, List<Integer> chooseluckySkills, int specialType, boolean useLuckyItem, boolean useCustomizedItem) {
        List<Integer> skills = new ArrayList<>();
        List<Integer> chooseGroups = new ArrayList<>();
        if (specialType > 0) {
            RuneRecastSkillTypeRow specialTypeRow = game.table.rune.specialRecast.get(specialType);
            if (specialTypeRow != null) {
                chooseGroups.addAll(useCustomizedItem ? specialTypeRow.luckyChooseSkillGroups : specialTypeRow.luckySkillNotUseItemGroups);
            }
        } else {
            chooseGroups.addAll(useCustomizedItem ? getTpl(game.fight.getPlayBuildContext()).luckyChooseSkillGroups : getTpl(game.fight.getPlayBuildContext()).luckySkillNotUseItemGroups);
        }
        if (chooseluckySkills.isEmpty()) {
            chooseluckySkills.addAll(game.table.rune.skill.getSkillsByGroups(chooseGroups));
        }
        if (!chooseGroups.isEmpty()) {
            EcResult<Void> r = checkSkills(game, chooseGroups, chooseluckySkills, useCustomizedItem);
            if (!r.ok()) {
                return skills;
            }
            if (useCustomizedItem) {
                for (Integer tid : chooseluckySkills) {
                    RuneSkillRow row = role.getGame().table.rune.skill.get(tid);
                    if (row != null) {
                        skills.add(row.skillId);
                    }
                }
                return skills;
            }
            RWList<SkillItem> list = new RWList<>();
            SkillItem sItem = list.addAndSetup(chooseluckySkills.stream().map(skillId -> {
                RuneSkillRow row = role.getGame().table.rune.skill.get(skillId);
                SkillItem skillItem = new SkillItem();
                skillItem.skillId = row.skillId;
                skillItem.weight = row.weight;
                return skillItem;
            }).collect(Collectors.toList())).get();
            //第一个必选技能
            skills.add(sItem.skillId);
        }
        List<Integer> groups = genSkillGroup(game, true, specialType);
        if (!groups.isEmpty()) {
            List<Integer> excludes = null;
            if (!skills.isEmpty()) {
                excludes = new ArrayList<>();
                SkillBaseRow base = game.table.skill.base.get(skills.get(0));
                excludes.add(base == null ? skills.get(0) : base.protoId);
            }
            skills.addAll(genSkill(game, groups, excludes));
        }
        return skills;
    }

    private List<Integer> genSkillGroup(GameBody game, boolean islucky, int specialType) {
        // 先判断是否需要读取特殊的技能重铸配置
        // 然后判断是从幸运技能组抽还是普通技能组抽
        SkillGroupList groupList = null;
        if (specialType > 0) {
            RuneRecastSkillTypeRow specialTypeRow = game.table.rune.specialRecast.get(specialType);
            if (specialTypeRow != null) {
                groupList = islucky ? specialTypeRow.luckySkillGroup : specialTypeRow.skillGroup;
            }
        }
        if (groupList == null) {
            groupList = islucky ? getTpl(game).luckySkillGroup : getTpl(game).skillGroup;
        }
        if (groupList != null && groupList.list != null) {
            // 原型技能不能重复
            return groupList.get().getGroupId();
        }
        return new ArrayList<>();
    }

    private List<Integer> genSkill(GameBody game, List<Integer> groups, List<Integer> excludeProtoIds) {
        List<Integer> skills = new ArrayList<>();
        Set<Integer> protoSet = new HashSet<>();
        if (excludeProtoIds != null) {
            protoSet.addAll(excludeProtoIds);
        }
        if (!groups.isEmpty()) {
            for (Integer groupId : groups) {
                RuneSkillRow skillRow = randomSkill(game, groupId, protoSet);
                if (skillRow != null) {
                    SkillBaseRow base = game.table.skill.base.get(skillRow.skillId);
                    protoSet.add(base == null ? skillRow.skillId : base.protoId);
                    skills.add(skillRow.skillId);
                }
            }
            if (skills.isEmpty()) {
                logger.error("徽章随机生成技能为空 tid={}", tid);
            }
        }
        return skills;
    }

    private List<Integer> genSkill(GameBody game, List<Integer> groups) {
        return genSkill(game, groups, null);
    }

    /**
     * 随机生成技能
     */
    public List<Integer> genSkill(GameBody game, boolean islucky, int specialType) {
        return genSkill(game, genSkillGroup(game, islucky, specialType));
    }

    private GameResult<Void> checkSkills(GameBody game, List<Integer> groupIds, List<Integer> skillIds, boolean useCustomizedItem) {
        GameResult<Void> r = new GameResult<>();
        if (useCustomizedItem && (CollectionUtils.isEmpty(skillIds) || skillIds.size() != 2)) {
            return r.fail("徽章技能选择有误");
        }
        Set<Integer> skillIdSets = new HashSet<>();
        for (Integer tid : skillIds) {
            if (skillIdSets.contains(tid)) {
                return r.fail("徽章技能选择有误");
            }
            RuneSkillRow runeSkillRow = game.table.rune.skill.get(tid);
            if (runeSkillRow == null) {
                return r.fail("徽章技能选择有误");
            }
            if (!groupIds.contains(runeSkillRow.groupId)) {
                return r.fail("徽章技能选择有误");
            }
            SkillBaseRow skillRow = game.table.skill.base.get(runeSkillRow.skillId);
            if (skillRow == null) {
                return r.fail("徽章技能选择有误");
            }
            skillIdSets.add(skillRow.protoId);
        }
        return r;
    }

    /**
     * 根据分组随机抽技能，同时过滤相同原型id的技能
     */
    private RuneSkillRow randomSkill(GameBody game, int groupId, Set<Integer> protoIds) {
        RWList<RuneSkillRow> rwl = new RWList<>();
        for (RuneSkillRow row : game.table.rune.skill.getList()) {
            SkillBaseRow base = game.table.skill.base.get(row.skillId);
            int protoId = base == null ? row.skillId : base.protoId;
            if (row.groupId == groupId && !protoIds.contains(protoId)) {
                rwl.add(row);
            }
        }
        rwl.setup();
        return rwl.get();
    }

    /**
     * 随机生成属性
     */
    @JsonIgnore
    public List<Integer> genAttr(GameBody game) {
        return genAttr(game, null);
    }

    @JsonIgnore
    public List<Integer> genAttr(GameBody game, List<Integer> customAttrs) {
        List<Integer> attrsId = new ArrayList<>();
        RuneBaseRow row = getTpl(game);
        if (row.attrGroup != null && !row.attrGroup.isEmpty()) {
            if (customAttrs != null && checkCustomAttrs(game, customAttrs, row.attrGroup, false)) {
                return customAttrs;
            }
            List<Integer> excludes = new ArrayList<>();
            for (Integer attrGrouId : row.attrGroup) {
                RuneAttrRow attrRow = game.table.rune.attr.randomAttr(attrGrouId, excludes);
                if (attrRow != null) {
                    attrsId.add(attrRow.tid);
                    excludes.add(attrRow.protoId);
                }
            }
            if (attrsId.isEmpty()) {
                logger.error("徽章随机生成属性为空 tid={}", tid);
            }
        }
        return attrsId;
    }

    private boolean checkCustomAttrs(GameBody game, List<Integer> customAttrs, List<Integer> attrGroup, boolean gradeUp) {
        if (customAttrs.size() != attrGroup.size()) {
            return false;
        }
        List<Integer> realGroups = new ArrayList<>();
        for (Integer attrId : customAttrs) {
            RuneAttrRow attrRow = game.table.rune.attr.get(attrId);
            if (attrRow == null) {
                return false;
            }
            realGroups.add(attrRow.groupId);
        }
        realGroups.sort(Comparator.comparingInt(e -> e));
        boolean groupCompare = StringUtils.equals(
                realGroups.toString(),
                attrGroup.stream().sorted(Comparator.comparingInt(e -> e)).collect(Collectors.toList()).toString());
        if (!groupCompare) {
            return false;
        }
        if (gradeUp) {
            for (Integer customAttr : customAttrs) {
                if (!role.getGame().table.rune.attr.isNiubiAttr(customAttr)) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 随机生成等级属性
     */
    public List<Integer> genLevelAttr(GameBody game, List<Integer> customAttrs) {
        List<Integer> attrsId = new ArrayList<>();
        RuneBaseRow row = getTpl(game);
        if (row.levelAttrRecastGroup != null) {
            if (customAttrs != null && checkCustomAttrs(game, customAttrs, row.levelAttrRecastGroup, true)) {
                return customAttrs;
            }
            for (Integer attrGrouId : row.levelAttrRecastGroup) {
                RuneAttrRow attrRow = game.table.rune.attr.randomAttr(attrGrouId);
                if (attrRow != null) {
                    attrsId.add(attrRow.tid);
                }
            }
        }
        return attrsId;
    }

    /**
     * 保存缓存
     */
    public void saveTemp(byte type) {
        if (GDRune.RecastType.recastSkill(type) && !reCast.skillIds.isEmpty()) {
            skillIds.clear();
            skillIds.addAll(reCast.skillIds);
            reCast.skillIds.clear();
        }
        if (GDRune.RecastType.recastAttr(type) && !reCast.attrIds.isEmpty()) {
            attrIds.clear();
            attrIds.addAll(reCast.attrIds);
            reCast.attrIds.clear();
        }
        if (GDRune.RecastType.recastLevelAttr(type) && !reCast.gradeAttrIds.isEmpty()) {
            gradeAttrIds.clear();
            gradeAttrIds.addAll(reCast.gradeAttrIds);
            reCast.gradeAttrIds.clear();
        }
    }

    /**
     * 被吞噬时提供的经验
     */
    @JsonIgnore
    public int getConsumeExp(GameBody game) {
        int e = 0;
        e += this.exp;
        RuneBaseRow row = getTpl(game);
        if (row != null) {
            e += row.baseExp;
        }
        if (level > 0) {
            e += game.table.rune.levelUp.calcExp(level);
        }
        return e;
    }

    public void addExp(PlayBuildContext ctx, int exp) {
        this.exp += exp;
        RuneLevelUpRow row = getNextLevelTpl(ctx);
        while (row != null && row.exp > 0 && this.exp >= row.exp) {
            this.exp -= row.exp;
            this.level += 1;
            row = getNextLevelTpl(ctx);
        }
    }

    public void onGradeUp(GameBody game) {
        RuneBaseRow row = this.getTpl(game);
        if (row == null) {
            return;
        }
        this.tid = row.nextLevelTid;
        row = this.getTpl(game);
        // 技能升级
        if (row.skillLevelUp) {
            int times = role.getGame().table.rune.base.getLevelUpTimes(row.quality, this.tid);
            if (times > 0) {
                levelUpSkill(game, times - 1);
            }
        }
        // 属性升级
        levelUpAttr(game);
        // 添加等级专属属性
        if (!row.gradeAttrGroups.isEmpty()) {
            for (Integer group : row.gradeAttrGroups) {
                RuneAttrRow attrRow = game.table.rune.attr.randomAttr(group);
                if (attrRow != null) {
                    gradeAttrIds.add(attrRow.tid);
                }
            }
        }
        // 清除缓存
        this.reCast.clear();
    }

    /**
     * 升级技能
     */
    public void levelUpSkill(GameBody game) {
        if (skillIds == null || skillIds.isEmpty()) {
            return;
        }
        // 找一个等级最低的技能进行升级
        int minLevel = 0;
        for (Integer skillId : skillIds) {
            int skillLevel = game.table.rune.skillScore.get(skillId).level;
            if (minLevel == 0 || skillLevel < minLevel) {
                minLevel = skillLevel;
            }
        }
        for (int i = 0; i < skillIds.size(); i++) {
            RuneSkillScoreRow row = game.table.rune.skillScore.getNextLevelRow(skillIds.get(i));
            if (row != null && row.level == minLevel + 1) {
                skillIds.set(i, row.id);
                return;
            }
        }
    }

    /**
     * 升级技能
     */
    public void levelUpSkill(GameBody game, int idx) {
        if (skillIds == null || skillIds.isEmpty()) {
            return;
        }
        RuneSkillScoreRow row = game.table.rune.skillScore.getNextLevelRow(skillIds.get(idx));
        if (row != null) {
            skillIds.set(idx, row.id);
        }
    }

    /**
     * 升级属性
     */
    public void levelUpAttr(GameBody game) {
        if (attrIds == null || attrIds.isEmpty()) {
            return;
        }
        Map<Integer, Integer> oldGroupAttrs = new HashMap<>();
        Set<Integer> protos = new HashSet<>();
        for (Integer attr : attrIds) {
            RuneAttrRow row = game.table.rune.attr.get(attr);
            protos.add(row.protoId);
            oldGroupAttrs.put(row.groupId, row.tid);
        }
        attrIds.clear();
        RuneBaseRow baseRow = getTpl(game);
        List<Integer> excludes = new ArrayList<>();
        for (Integer group : baseRow.attrGroup) {
            if (oldGroupAttrs.containsKey(group)) {
                int attrId = oldGroupAttrs.get(group);
                attrIds.add(attrId);
                RuneAttrRow row = game.table.rune.attr.get(attrId);
                excludes.add(row.protoId);
                oldGroupAttrs.remove(group);
                continue;
            }
            boolean hasProto = false;
            if (!oldGroupAttrs.isEmpty()) {
                for (Integer proto : protos) {
                    RuneAttrRow attrRow = game.table.rune.attr.getAttr(group, proto);
                    if (attrRow != null) {
                        attrIds.add(attrRow.tid);
                        hasProto = true;
                    }
                }
            }
            if (!hasProto) {
                RuneAttrRow attrRow = game.table.rune.attr.randomAttr(group, excludes);
                if (attrRow != null) {
                    attrIds.add(attrRow.tid);
                }
            }
        }
    }

    public List<Integer> buildSkillLevel(GameBody game) {
        List<Integer> levels = new ArrayList<>();
        for (Integer skillId : skillIds) {
            RuneSkillScoreRow row = game.table.rune.skillScore.get(skillId);
            if (row != null) {
                levels.add(row.level);
            }
        }
        return levels;
    }

    @Override
    public RuneBaseRow getTpl(PlayBuildContext ctx) {
        return ctx.runeExcel().base.get(tid);
    }

    @Override
    public RuneBaseRow getTpl(GameBody game) {
        return getTpl(game.fight.getPlayBuildContext());
    }

    private RuneLevelUpRow getNextLevelTpl(PlayBuildContext ctx) {
        return ctx.runeExcel().levelUp.get(level);
    }

    public FightAttr getPowerAttr(PlayBuildContext ctx) {
        FightAttr attr = new FightAttr();
        if (attrIds != null && !attrIds.isEmpty()) {
            for (int i = 0; i< attrIds.size(); ++i) {
                int tid = attrIds.get(i);
                if (i == 0 && this.tid >= 10003) {
                    attr.add(new FightAttr(ctx.runeExcel().attr.get(tid).attrStr).scale(2));
                } else  {
                    attr.add(new FightAttr(ctx.runeExcel().attr.get(tid).attrStr));
                }
            }
            if (level > 0) {
                RuneLevelUpRow levelUpRow = ctx.runeExcel().levelUp.get(level);
                if (levelUpRow != null) {
                    int attrRowId = attrIds.get(0);
                    RuneAttrRow row = ctx.runeExcel().attr.get(attrRowId);
                    if (row != null) {
                        int attrId = Integer.parseInt(row.attrStr.split(":")[0]);
                        int value = levelUpRow.attrs.getOrDefault(attrId, 0);
                        if (value > 0) {
                            attr.add(new FightAttr(attrId + ":" + value));
                        }
                    }
                }
            }
        }
        if (gradeAttrIds != null && !gradeAttrIds.isEmpty()) {
            for (Integer id : gradeAttrIds) {
                attr.add(new FightAttr(ctx.runeExcel().attr.get(id).attrStr));
            }
        }
        return attr;
    }

    @Override
    public Rune copy() {
        Rune cp = new Rune();
        super.copy(cp);
        cp.attrIds = ListKit.cloneList(this.attrIds, Integer::intValue);
        cp.skillIds = ListKit.cloneList(this.skillIds, Integer::intValue);
        cp.reCast = this.reCast != null ? this.reCast.copy() : null;
        cp.gradeAttrIds = ListKit.cloneList(this.gradeAttrIds, Integer::intValue);
        cp.initialize(null);
        return cp;
    }

    @Override
    public String toDetail() {
        return String.valueOf(level);
    }

    private static final class SkillItem implements RWItem {
        public int skillId;
        public double weight;

        @Override
        public double getWeight() {
            return weight;
        }
    }
}
